////////////////////////////////////////////////////////////////////////////////
//
//	Original file Name: GameFrame.java
//	Originating Author: JWhitefield
//	
//	Description: Main Frame for the game 
//
////////////////////////////////////////////////////////////////////////////////

package com.jjw.game;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.Graphics;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferStrategy;

import javax.swing.JFrame;

import com.jjw.game.input.InputManager;
import com.jjw.game.screen.ScreenController;
import com.jjw.game.world.tile.Tile;
import com.jjw.game.world.tile.TileManager;

/**
 * Main Frame for the game 
 * @author JWhitefield
 *
 */
@SuppressWarnings("unused")
public class Game extends Canvas implements WindowListener,  Runnable {

	
	//--------------------------------------------------------------------------
	//
	//Properties
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	// Public
	//----------------------------------
	
	/**
	 * Flag to put game into debug mode
	 */
	public static boolean DEBUG = true;
	
	/**
	 * What's the name of the game
	 */
	public static final String NAME = "RPG TEST";
	
	/**
	 * Height of the application 
	 */
	public static final int HEIGHT = 120;
	
	/**
	 * Width of the application 
	 */
	public static final int WIDTH = 160;
			
	/**
	 * Paused variable
	 */
	public static boolean PAUSED = false;
	
	/**
	 * The display mode 
	 */
	public DisplayMode displayMode;
	
	//----------------------------------
	//  Getters / Setters
	//----------------------------------
	
	/**
	 * Get the width of the game
	 */
	public static int gameWidth() {
		return WIDTH * SCALE;
	}
	
	/**
	 * Get the height of the game 
	 */
	public static int gameHeight() {
		return HEIGHT * SCALE;
	}
	
	//----------------------------------
	// Private
	//----------------------------------
	
	/**
	 * Serial version uid
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * Scale of the application 
	 */
	private static final int SCALE = 5;
	
	/**
	 * The amount of ticks since the game has started
	 */
	private int tickCount = 0;
	
	/**
	 * The input manager deals with all the controllers use within the application 
	 */
	private InputManager inputManager = InputManager.getInstance();
	
	/**
	 * Games frame
	 */
	private JFrame frame = null;
	
	//--------------------------------------------------------------------------
	//
	//  Methods
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	// Public
	//----------------------------------
	
	/**
	 * Main methods 
	 * @param args
	 * 
	 */
	public static void main(String[] args) {
			
		//Create a new instance of the frame 
		Game game = new Game();
		
		//Start the game
		game.startGame();
	}
	
	/**
	 * Start the game 
	 */
	public void startGame(){
	
		//Create a new thread as this and start  it
		new Thread(this).start();
	}
	
	public void init(){
		
		//Create a game size dimension
		Dimension gameSize = new Dimension( gameWidth() - 9, gameHeight() - 9);
		
		//Set the bounds of the canvas we extended 
		this.setMinimumSize( gameSize );
		this.setMaximumSize( gameSize );
		this.setPreferredSize( gameSize );

		//Create the JFrame that this game canvas will sit in 
		this.frame = new JFrame(Game.NAME);
		this.frame.addWindowListener(this);
		this.frame.setLayout(new BorderLayout());
		this.frame.add(this, BorderLayout.CENTER);
		this.frame.pack();
		this.frame.setResizable(false);
		this.frame.setLocationRelativeTo(null);
		this.frame.setVisible(true);
	}
	
	/**
	 * Exit the game
	 */
	public static void exit() {

	}
	
	//----------------------------------
	// Private
	//----------------------------------
	
	/**
	 * Called to tick the game over
	 */
	private void tick() {

		//Up the tick count 
		this.tickCount++;
		
		//Does the screen have focus 
		if (! this.hasFocus() ) {
			
			//Release the input
			this.inputManager.resetInputDevicesButtons();
		}else{
			
			//Tick the input controls
			this.inputManager.tick();
			
			//Tick the screen controller
			ScreenController.INSTANCE.tick();
		}
	}

	/**
	 * Called when we want to render the game on-screen
	 */
	private void render() {
					
		//Get the buffer strategy 
		BufferStrategy bs = this.getBufferStrategy();
		
		//Check if we have one
		if (bs == null) {
			
			//If we don't have one create one 
			this.createBufferStrategy(3);
			
			//Request focus
			this.requestFocus();
			
			//Return out
			return;
		}
				
		//Get the graphics 
		Graphics g = bs.getDrawGraphics();
				
		//Fill the background
		g.fillRect(0, 0, getWidth(), getHeight());
		
		//Render the graphics 
		ScreenController.INSTANCE.render( g );
		
		//Dispose of the graphics 
		g.dispose();
		
		//Show the buffer strategy 
		bs.show();		
	}
	
	/**
	 * Gained focus of the application
	 */
	private void gainedFocus() {
		
		
	}
	
	/**
	 * Lost focus of the application 
	 */
	private void lostFocus() {
		
	}

	//----------------------------------
	// Overridden / Implemented
	//----------------------------------
	
	@Override
	/**
	 * Called when we start the thread 
	 */
	public void run() {
		
		//Get the last time in nano seconds 
		long lastTime = System.nanoTime();
		
		//Amount of unprocessed ticks
		double unprocessedTicks = 0;
		
		//Amount of nanoseconds per tick
		double nsPerTick = 1000000000.0 / 60;
		
		//How many frames 
		int frames = 0;
		
		//Tick count
		int ticks = 0;
		
		//Used to show the FPS of the application 
		long fpsDisplayTime = System.currentTimeMillis();

		//Initialises the window before we get everything going
		this.init();

		//We always want to be rendering so an infinite loop till we exit the game
		while ( true ) {
			
			//Get now in nanoseconds
			long now = System.nanoTime();
			
			//Amount of unprocessed ticks
			unprocessedTicks += (now - lastTime) / nsPerTick;
			
			//Set the last time to now
			lastTime = now;
			
			//Should we want to render now 
			boolean render = true;
			
			//If the game is paused then we need to do the following
			if( PAUSED ) {

				//Pause the loop for a short time
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {}
				
				//If we are running in debug then print the loop state
				if( DEBUG )
					System.out.println("PAUSED");
				
				//Continue the loop on 
				continue;
			}
				
			
			//Wilst we have unprocessed ticks 
			while (unprocessedTicks >= 1) {
				
				//Up the tick count and make the game tick
				ticks++;
				
				//Tick the game
				this.tick();
				
				//Now we have processed the tick lower the tick count
				unprocessedTicks -= 1;
				
				//Now we want to render
				render = true;
			}

			//Try to sleep the thread for a short time
			try {
				Thread.sleep(2);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			//If we are rendering 
			if (render) {
				
				//Up the frame count and render the game
				frames++;
				this.render();
			}
			
			//If a second has passed then we want to show the FPS and tick count
			if (System.currentTimeMillis() - fpsDisplayTime > 1000) {				
				//Update the time 
				fpsDisplayTime += 1000;

				//Print the FPS and ticks to the command line
				if( DEBUG ) {
					//Display the FPS
					System.out.println(ticks + " ticks, " + frames + " fps");					
				}
				
				//Reset the counts
				frames = 0;
				ticks = 0;
			}
		}
	}

	@Override
	public void windowActivated(WindowEvent e) {
		
		//Set the paused flag
		PAUSED = false;
	}

	@Override
	public void windowClosed(WindowEvent e) {
		
		//Exit the game
		System.exit(0);		
	}

	@Override
	public void windowClosing(WindowEvent e) {
		
		//Exit the game
		System.exit(0);	
	}

	@Override
	public void windowDeactivated(WindowEvent e) {
		
		//Set paused flag
		PAUSED = true;
	}

	@Override
	public void windowDeiconified(WindowEvent e) {
				
		//Set the paused flag 
		PAUSED = false;
	}

	@Override
	public void windowIconified(WindowEvent e) {
		
		//Set the paused flag
		PAUSED = true;
	}

	@Override
	public void windowOpened(WindowEvent e) {
		
	}
}
